/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: xmessage.h
 *
 * Purpose: message Base API define and implementation
 *
 * Developer:
 *   wen.gu , 2023-07-26
 *
 * TODO:
 *
 ***************************************************************************/

#ifndef __XBUS_CORE_XMESSAGE_H__
#define __XBUS_CORE_XMESSAGE_H__

#include <cstdint>
#include <memory>
#include <cstring>

#include "xbus/core/xtype.h"
#include "xbus/core/xqueue.h"
#include "xbus/com/xtypes.h"
#include "xbus/com/xbuffer.h"


#define XMSG_START_CODE 0xabcd
#define XMSG_START_CODE_BYTE1 0xcd
#define XMSG_START_CODE_BYTE2 0xab

#define XMSG_PROTOCOL_VER (1)
/**unkown message type */
#define XMSG_UNKNOWN_TYPE (0)
#define XMSG_GET_PROTOCOL_VER(flags) ((flags) & 0x0f)


#define XMSG_FLAGS_TP_FLAG_MASK (0x80)
#define XMSG_FLAGS_MASK (0xf0)

#define XMSG_FLAGS_TP_FLAG(flags) ((flags) & XMSG_FLAGS_TP_FLAG_MASK)
#define XMSG_FLAGS(flags) ((flags) & XMSG_FLAGS_MASK)

#define XMSG_FLAGS_TP_FLAG_ENABLE(flags)  do{flags |= XMSG_FLAGS_TP_FLAG_MASK;}while(0)
#define XMSG_FLAGS_TP_FLAG_DISABLE(flags) do{flags &= (~XMSG_FLAGS_TP_FLAG_MASK);}while(0)


/**for base header */
#define XMSG_FIELD_OFFSET_LENGTH (0)
#define XMSG_FIELD_OFFSET_STARTCODE (4)
#define XMSG_FIELD_OFFSET_FLAGS (6)
#define XMSG_FIELD_OFFSET_TYPE (7)
#define XMSG_FIELD_OFFSET_MSG_ID (8)
#define XMSG_FIELD_OFFSET_SESSION_ID (10)

/** for extension header about communication protocol(unpacking/pacakging) */
#define XMSG_FIELD_OFFSET_PKT_TOTAL (sizeof(xbus::core::XMessageHeader))
#define XMSG_FIELD_OFFSET_PKT_IDX   (XMSG_FIELD_OFFSET_PKT_TOTAL + 4)

#define XMSG_PKT_IDX_START (1)
#define XMSG_PKT_IDX_INVALID (0)

#define XMSG_INVALID_ENDPOINT_ID (-1)


namespace xbus::core {

using MessageId = uint16_t;
using SequenceNumber = uint16_t;
using XEndpointId = uint64_t;


struct XMessageHeader {
    uint32_t length;  /** total message size = header size + payloaf size */
    uint16_t start_code;
    /**
     * bit 4~7:   flags,
     *      - bit 7:  tp(transport) flag: 
     *                  - 1: indicat a big packet will sent to target by split to many segments(e.g communication with udp). 
     *                  - 0: just a normal message
     *      - bit 4~6: reserved
     * bit 0~3: the protocol version. range is: 1 ~ 15, now is 1
    */
    uint8_t flags;
    uint8_t type;
    MessageId message_id;
    SequenceNumber seq_num;
};

/** Extension Header items define */
struct XMsgHdrExtPktInfo {
    uint32_t total; /** a big message split to how many packet */
    uint32_t idx;   /** the index of current packet in a big message */
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace internal {
static inline uint32_t ByteRead4(const uint8_t* pdata) { return pdata[0] | (pdata[1] << 8) | (pdata[2] << 16) | (pdata[3] << 24); }
static inline uint16_t ByteRead2(const uint8_t* pdata) { return pdata[0] | (pdata[1] << 8); }

static inline void ByteWrite2(uint8_t* buf, uint16_t val) { memcpy(buf, &val, sizeof(val)); }
static inline void ByteWrite4(uint8_t* buf, uint32_t val) { memcpy(buf, &val, sizeof(val)); }

} //namespace internal




////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/** message structure spec
 * | length(4) | start_code(2) | flags(1) | type(1) | message id(2) | sequence number(2) | extension(dynamic)| payload (dynamic)| 
 * 
 * Header Base: 12 byte
 * 
 */


class XMessage;

using XMessageSp = std::shared_ptr<XMessage>;

class XMessage {
public:
    using XEndpointIdArray = std::vector<XEndpointId>;
    using XMessageList = std::vector<XMessageSp>;
public:
    virtual ~XMessage() = default;
public: /** [mandatory] base header operation */
    virtual uint8_t* data() = 0; /** message buffer start */
    virtual uint32_t length() const = 0;
    virtual uint16_t start_code() const = 0;
    virtual uint8_t flags() const = 0;
    virtual uint8_t protocol_version() const = 0;
    virtual uint8_t type() const = 0;
    virtual MessageId message_id() const = 0;
    virtual SequenceNumber seq_num() const = 0;

    virtual void set_type(uint8_t type) = 0;
    virtual void set_message_id(MessageId message_id) = 0;
    virtual void set_seq_num(SequenceNumber seq_num) = 0;

public: /** [optional] packet information operation, multi packet info for a big message (extension header) */
    virtual bool is_tp_message() const = 0;
    virtual uint32_t pkt_total() const = 0;
    virtual uint32_t pkt_idx() const = 0;

    virtual void set_pkt_total(uint32_t total) = 0;
    virtual void set_pkt_idx(uint32_t idx) = 0;

public: /** payload operation */
    virtual uint8_t* payload() const = 0;
    virtual uint32_t payload_size() const = 0;

public:
    virtual XMessageList unpack(uint32_t pkt_size) = 0;

public: /** helper API for message send/receive */
    virtual XEndpointId msg_src() const { return msg_src_; }
    virtual const XEndpointIdArray& msg_dst() const { return msg_dst_; }

    virtual void set_msg_src(XEndpointId src_id) { msg_src_ = src_id; }
    virtual void add_msg_dst(XEndpointId dst_id) { msg_dst_.push_back(dst_id); }
    virtual void set_msg_dst(const XEndpointIdArray& dst_ids) { msg_dst_ = dst_ids; }

public: /** helper API for Parse Message */
    static bool CheckMessageHeader(const uint8_t* data, uint32_t size);
    static bool CheckMessageHeader(const XMessageHeader& hdr);
    static uint32_t GetHeaderLength(uint8_t flags);

    static void InitializeMessageHeader(uint8_t* data, uint32_t length, uint8_t flags);

protected:
    XEndpointId msg_src_ = XMSG_INVALID_ENDPOINT_ID;
    XEndpointIdArray msg_dst_;
};

using XMessageQueue = core::XQueue<XMessageSp>;
using XMessageQueueSp = std::shared_ptr<XMessageQueue>;


inline XMessageQueueSp XMessageQueueCreate(uint32_t max_limit = 0){  /** if set as 0, inidicate unlimited this size */
    return std::make_shared<XMessageQueue>(max_limit);
}



} //namespace xbus::core


#endif /** !__XBUS_CORE_XMESSAGE_H__ */